package com.zzhua.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.*;

public class MyGenerator {

    public static void main(String[] args) throws Exception{

        // 生成字节码class文件
        generateClass();

        // 加载生成的class文件,并调用
        loadAsmGeneratedClassAndInvoke();

    }

    private static void loadAsmGeneratedClassAndInvoke() throws Exception {
        //读取本地的class文件内的字节码，转换成字节码数组
        File file = new File(".");
        InputStream  input = new FileInputStream(file.getCanonicalPath()
                +"\\src\\main\\java\\com\\zzhua\\asm\\HelloAsm.class");
        byte[] result = new byte[1024];

        int count = input.read(result);
        // 使用自定义的类加载器将 byte字节码数组转换为对应的class对象
        MyClassLoader loader = new MyClassLoader();
        Class clazz = loader.defineMyClass( result, 0, count);
        //测试加载是否成功，打印class 对象的名称
        System.out.println(clazz.getCanonicalName());

        //实例化一个HelloAsm对象
        Object o= clazz.newInstance();
        //调用HelloAsm的hello方法
        clazz.getMethod("hello", null).invoke(o, null);

    }

    private static void generateClass() throws IOException {
        ClassWriter classWriter = new ClassWriter(0);
        // 通过visit方法确定类的头部信息
        classWriter.visit(Opcodes.V1_7,// java版本
                Opcodes.ACC_PUBLIC,// 类修饰符
                "HelloAsm", // 类的全限定名
                null, "java/lang/Object", null);

        //创建构造函数
        MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // 定义code方法
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "hello", "()V",null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out","Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("Hello,asm~");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println","(Ljava/lang/String;)V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        classWriter.visitEnd();
        // 使classWriter类已经完成
        // 将classWriter转换成字节数组写到文件里面去
        byte[] data = classWriter.toByteArray();
        File file = new File("D:\\Projects\\learning\\proxy-dynamic-demo\\src\\main\\java\\" +
                "com\\zzhua\\asm\\HelloAsm.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();
    }
}

/**
 * 自定义一个类加载器，用于将字节码转换为class对象
 */
class MyClassLoader extends ClassLoader {

    public Class<?> defineMyClass( byte[] b, int off, int len)
    {
        return super.defineClass(b, off, len);
    }

}
